import os
import time
import model
import datetime
import component
import traceback
import resource.string.app

from _console import Console
from progressbar import (AnimatedMarker, Bar, BouncingBar, Counter, ETA,
                         FileTransferSpeed, FormatLabel, Percentage,
                         ProgressBar, ReverseBar, RotatingMarker,
                         SimpleProgress, Timer)

class Downloader(Console):
    """Konata Downloader

    Download image data and image from booru.
    """

    def run(self):
        """Main program"""

        self.greet(resource.string.app)

        self.initFolder()
        self.db = self.openDb()
        self.download()

        self.farewell()

    _hashGauge = None

    def gaugeHash(self, total, progress):
        """Show or update progress bar for file hashing operation.

        Args:
            total: An integer determine total job to be done.
            progress: An integer indicating total progress so far.
        """

        if self._hashGauge is None:
            bar = Bar(left='[', right=']')
            widgets = ['Hashing MD5 ', bar, ' ', Percentage(),' ', ETA()]
            self._hashGauge = ProgressBar(widgets=widgets, maxval=total)
            self._hashGauge.start()

        if progress > total: progress = total
        self._hashGauge.update(progress)

        if progress >= total:
            self._hashGauge.finish()
            self._hashGauge = None

    _downloadGauge = None

    def gaugeDownload(self, total, progress):
        """Show or update progress bar for download operation.

        Args:
            total: An integer determine total job to be done.
            progress: An integer indicating total progress so far.
        """

        if self._downloadGauge is None:
            speed = FileTransferSpeed()
            if total:
                size = component.Formatter.byte(total)
                bar = Bar(left='[', right=']')
                widgets = [size, ' @ ', speed, ' ', bar, ' ', Percentage(),' ',
                           ETA()]
                self._downloadGauge = ProgressBar(widgets=widgets, maxval=total)
            else:
                widgets = [speed, ' ', BouncingBar(), ' ', Timer()]
                self._downloadGauge = ProgressBar(widgets=widgets)

            self._downloadGauge.start()

        if progress > total: progress = total
        self._downloadGauge.update(progress)

        if progress >= total:
            self._downloadGauge.finish()
            self._downloadGauge = None

    def download(self):
        """The actual download program."""

        messages = {'downloading': 'Downloading image #%s of %s from %s...\n',
                    'corrupt': "MD5 didn't match! Mark image as corrupted.\n",
                    'dead': '%s marked as dead. Skip it.\n',
                    'fail': '\nDownload failed.\n',
                    'exists': '\nImage already exists. Skip it.\n',
                    'abort': ('\nKeyboard interupt detected. '
                              'Operation aborted.\n'),
                    'delete': 'Deleting courrupted file...',
                    'corrupted': ("Image downloaded but MD5 didn't match. "
                                  "Skip it.\n")}

        Image = model.Image
        query = self.db.query(Image)
        images = query.filter(Image.status == Image.STATUS_QUEUED)

        # The line below is for testing purpose.
        # The image is quite small so it won't hurt to download.
##        images = query.filter(Image.id == Image.id == 195)

        total = "{:,}".format(images.count())
        aborted = False

        for index, image in enumerate(images.all()):
            for booru in image.boorus:
                d = ("{:,}".format(index + 1), total, booru.host.name)
                self.yell(messages['downloading'] % d)

                if booru.host.status == model.Host.STATUS_DEAD:
                    self.warning(messages['dead'] % booru.host.name)
                    continue

                if image.file.exists:
                    self.yell(messages['exists'])
                    image.file.reportHook = self.gaugeHash
                    if image.file.md5 == image.md5:
                        image.download()
                        self.db.commit()
                        continue
                    else:
                        self.progress('delete')
                        try:
                            image.file.unlink()
                            self.cheer('OK ^_^\n')
                        except:
                            self.fail('Error >_<\nSkipping it.\n')
                            continue

                d = (image.md5, booru.host.name, image.extension)
                corruptFilename = '%s - %s - Corrupt.%s' % d
                corruptFilename = os.path.join('temp', corruptFilename)
                if os.path.lexists(corruptFilename):
                    self.yell(messages['corrupted'])
                    continue

                dowloaded = False
                self.progress('Retrieving image information from server...\r')

                try:
                    dowloaded = booru.download(self.gaugeDownload)
                except KeyboardInterrupt:
                    self.fail(messages['abort'])
                    aborted = True
                except:
                    if self._verbose:
                        print
                        print traceback.format_exc()

                if not dowloaded:
                    for i in range(10):
                        try:
                            image.temp.unlink()
                            break
                        except:
                            time.sleep(0.1)
                    self.fail(messages['fail'])
                    self._downloadGauge = None

                if image.temp.exists:
                    image.temp.reportHook = self.gaugeHash
                    if image.temp.md5 == image.md5:
                        try: os.rename(image.temp.path, image.file.path)
                        except: pass
                        image.download()
                        self.db.commit()

                        # if download succeed, break from booru loop.
                        break
                    else:
                        self.warning(messages['corrupt'])

                        try: os.rename(image.temp.path, corruptFilename)
                        except: pass

                if aborted: return
